#include "bsp.h"


#define OLED_RES_0()
#define OLED_RES_1()

#define OLED_CS_0()
#define OLED_CS_1()

#define OLED_DC_0()
#define OLED_DC_1()


#define OLED_DELAY_MS  bsp_delay_ms

typedef enum 
{
    OLED_CMD = 0,
    OLED_DATA
}oled_data_type_e;


/* OLED GRAM 缓冲 */
static uint8_t _oled_gram[OLED_X_POS_MAX][OLED_Y_POS_MAX / 8];

/***************************************************************************************************
 * @fn      oled_write_byte
 *
 * @brief   向OLED写入一个数据
 *
 * @param   data 要写入的数据
 * @param   data_type 写入的数据类型
 *
 * @return  无
 */
static void oled_write_byte(uint8_t data, oled_data_type_e data_type)
{
    if (OLED_CMD == data_type)
    {
        OLED_DC_0();  /* DC输出低电平：读写命令 */
    }
    else if (OLED_DATA == data_type)
    {
        OLED_DC_1();  /* DC输出低电平：读写数据 */
    }
    else
    {
        return;
    }

    /* spi选中 */
    OLED_CS_0();  

    /* 使用spi接口发送数据/命令 */
    oled_spi_rw_byte(data);  

    /* spi取消选中 */
    OLED_CS_1();  

    /* OLED的DC输出高电平 */
    OLED_DC_1();  
}

/***************************************************************************************************
 * @fn      config_oled_reg
 *
 * @brief   对OLED的关键寄存器做初始化配置
 *
 * @param   无
 *
 * @return  无
 */
static void config_oled_reg(void)
{
    oled_write_byte(0xAE, OLED_CMD);  /* 关闭显示 */

    oled_write_byte(0xD5, OLED_CMD);  /* 设置时钟分频因子，振荡频率 */
    oled_write_byte(0x50, OLED_CMD);  /* [3:0]为分频因子，[7:4]为振荡频率 */

    oled_write_byte(0xA8, OLED_CMD);  /* 设置驱动路数 */
    oled_write_byte(0x3F, OLED_CMD);  /* 默认0x3F（1/64） */ 

    oled_write_byte(0xD3, OLED_CMD);  /* 设置显示偏移 */
    oled_write_byte(0x00, OLED_CMD);  /* 默认为0 */

    oled_write_byte(0x40, OLED_CMD);  /* 设置显示开始行，[5:0]为行数 */

    oled_write_byte(0x8D, OLED_CMD);  /* 设置电荷泵 */
    oled_write_byte(0x14, OLED_CMD);  /* bit2用于设置开启（1）/关闭（0） */

    oled_write_byte(0x20, OLED_CMD);  /* 设置内存地址模式 */
    oled_write_byte(0x02, OLED_CMD);  /* [1:0]，00-列地址模式，01-行地址模式，10-页地址模式（默认值） */

    oled_write_byte(0xA1, OLED_CMD);  /* 设置段重定义，bit0为0，列地址0->SEG0，bit0为1，列地址0->SEG127 */

    oled_write_byte(0xC0, OLED_CMD);  /* 设置COM扫描方向，bit3为0，普通模式，bit3为1，重定义模式 */

    oled_write_byte(0xDA, OLED_CMD);  /* 设置COM硬件引脚配置 */
    oled_write_byte(0x12, OLED_CMD);  /* [5:4]为硬件引脚配置信息 */

    oled_write_byte(0x81, OLED_CMD);  /* 设置对比度 */
    oled_write_byte(0xEF, OLED_CMD);  /* 1～255，默认为0x7F（亮度设置，越大越亮） */

    oled_write_byte(0xD9, OLED_CMD);  /* 设置预充电周期 */
    oled_write_byte(0xf1, OLED_CMD);  /* [3:0]为PHASE1，[7:4]为PHASE2 */

    oled_write_byte(0xDB, OLED_CMD);  /* 设置VCOMH电压倍率 */
    oled_write_byte(0x30, OLED_CMD);  /* [6:4]，000-0.65*vcc，001-0.77*vcc，011-0.83*vcc */

    oled_write_byte(0xA4, OLED_CMD);  /* 全局显示开启，bit0为1，开启，bit0为0，关闭 */

    oled_write_byte(0xA6, OLED_CMD);  /* 设置显示方式，bit0为1，反相显示，bit0为0，正常显示 */ 

    oled_write_byte(0xAF, OLED_CMD);  /* 开启显示 */
}

/***************************************************************************************************
 * @fn      oled_init
 *
 * @brief   初始化OLED
 *
 * @param   无
 *
 * @return  无
 */
void oled_init(void)
{
    /* 初始化spi接口 */
    oled_spi_init();

    /* 初始化GPIO */
    ;

    OLED_RES_0();
    OLED_DELAY_MS(10);
    OLED_RES_1();
    OLED_DELAY_MS(10);

    /* 对OLED的关键寄存器做初始化配置 */
    config_oled_reg();
}

/***************************************************************************************************
 * @fn      oled_draw_point
 *
 * @brief   OLED画点
 *
 * @param   x 这个点的x坐标（横坐标）
 * @param   y 这个点的y坐标（纵坐标）
 * @param   state 这个点的状态，为真则点亮，为假则为灭
 *
 * @return  无
 */
void oled_draw_point(uint8_t x, uint8_t y, uint8_t state)
{
    uint8_t pos;
    uint8_t bx;
    uint8_t temp = 0;

    /* 超出屏幕范围 */
    if ((x > (OLED_X_POS_MAX - 1)) || (y > (OLED_Y_POS_MAX - 1)))
    {
        return;
    }

    pos = 7 - y / 8;  /* 求指定位置所在页数 */
    bx = y % 8;  /* 求行号 */
    temp = 1U << (7 - bx);  /* （7-bx）求出相应SSD1306的行号，并在字节中相应的位置为1 */

    if (state)
    {
        _oled_gram[x][pos] |= temp;
    }
    else
    {
        _oled_gram[x][pos] &= ~temp;
    }
}

/***************************************************************************************************
 * @fn      oled_read_point
 *
 * @brief   读取OLED一个点的状态：1还是0
 *
 * @param   x 这个点的x坐标（横坐标）
 * @param   y 这个点的y坐标（纵坐标）    
 *
 * @return  这个点的状态，为真则点亮（1），为假则为灭（0）
 */
uint8_t oled_read_point(uint8_t x, uint8_t y)
{
    uint8_t pos;
    uint8_t bx;
    uint8_t temp = 0;
    uint8_t state;

    /* 超出屏幕范围 */
    if ((x > (OLED_X_POS_MAX - 1)) || (y > (OLED_Y_POS_MAX - 1)))
    {
        return 0;
    }

    pos = 7 - y / 8;  /* 求指定位置所在页数 */
    bx = y % 8;  /* 求行号 */
    temp = 1U << (7 - bx);  /* （7-bx）求出相应SSD1306的行号，并在字节中相应的位置为1 */

    if (_oled_gram[x][pos] & temp)
    {
        state = 1;
    }
    else
    {
        state = 0;
    }

    return state;
}

/***************************************************************************************************
 * @fn      oled_clear
 *
 * @brief   清除屏幕所有数据
 *
 * @param   无
 *
 * @return  无
 */
void oled_clear(void)
{
    uint8_t i;
    uint8_t n;

    for (i = 0; (i < (OLED_Y_POS_MAX / 8)); i++)  /* 遍历每一页 */
    {
        for (n = 0; n < OLED_X_POS_MAX; n++)  /* 遍历每一页 */
        {
            _oled_gram[n][i] = 0;
        }
    }
}

/***************************************************************************************************
 * @fn      oled_refresh
 *
 * @brief   刷新屏幕数据
 *
 * @param   无
 *
 * @return  无
 */
void oled_refresh(void)
{
    uint8_t i;
    uint8_t n;

    for (i = 0; (i < (OLED_Y_POS_MAX / 8)); i++)  /* 遍历每一页 */
    {
        oled_write_byte((0xB0 + i), OLED_CMD);
        oled_write_byte(0x00, OLED_CMD);
        oled_write_byte(0x10, OLED_CMD);

        for (n = 0; n < OLED_X_POS_MAX; n++)  /* 遍历每一页 */
        {
            oled_write_byte(_oled_gram[n][i], OLED_DATA);
        }
    }
}

/***************************************************************************************************
 * @fn      oled_display_off
 *
 * @brief   关闭显示
 *
 * @param   无
 *
 * @return  无
 */
void oled_display_off(void)
{
    /* 打开关闭电荷泵，第一字节为命令字，0x8D，第二字节设置值，0x10-关闭电荷泵，0x14-打开电荷泵 */
    oled_write_byte(0x8D, OLED_CMD);  /* 第一字节为命令字，0x8D */
    oled_write_byte(0x10, OLED_CMD);  /* 0x10-关闭电荷泵 */

    /* 设置显示开关，0xAE-关闭显示，0xAF-开启显示 */
    oled_write_byte(0xAE, OLED_CMD);
}

/***************************************************************************************************
 * @fn      oled_display_on
 *
 * @brief   开启显示
 *
 * @param   无
 *
 * @return  无
 */
void oled_display_on(void)
{
    /* 打开关闭电荷泵，第一字节为命令字，0x8D，第二字节设置值，0x10-关闭电荷泵，0x14-打开电荷泵 */
    oled_write_byte(0x8D, OLED_CMD);  /* 第一字节为命令字，0x8D */
    oled_write_byte(0x14, OLED_CMD);  /* 0x14-打开电荷泵 */

    /* 设置显示开关，0xAE-关闭显示，0xAF-开启显示 */
    oled_write_byte(0xAF, OLED_CMD);
}


